﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Google.Protobuf;
using LitJson;
using mb;

namespace libmp
{

    public class User
    {
        static ILog logger = App.logger;
        static API.IStartOption startOption = App.StartOption;

        //版本号，写到保存文件里去，以便支持升级兼容
        static int VERSION_20190602 = 20190602;
        static int VERSION_NOW = VERSION_20190602;
        //我的信息保存路径 

        public delegate void OnChangedType(User user);
        public OnChangedType OnChanged;

        Mb.UserInfo pbo;
        public string localPath;
        public string token;
        public int version { get => pbo.Version; set => pbo.Version = value; }
        public string id { get => pbo.Id; set => pbo.Id = value; }
        public string name { get => pbo.Name; set => pbo.Name = value; }
        public string phone { get => pbo.Phone; set => pbo.Phone = value; }
        public string email { get => pbo.Email; set => pbo.Email = value; }
        public float balance { get => pbo.Balance; set => pbo.Balance = value; }
        public int userLevel { get => pbo.Level; set => pbo.Level = value; }
        // public string userHeadPic { get => pbo.Id; set => pbo.Id = value; }
        // public string dueDate { get => pbo.Id; set => pbo.Id = value; }
        // public long changeDate { get => pbo.Id; set => pbo.Id = value; }
        public IList<int> SeenVideoList { get => pbo.SeenVideoList; }
        public Mb.CountPair VideoCanPlay { get => pbo.VideoCanPlay; }
        public int VideoCanPlayRemain { get => VideoCanPlay.Max - VideoCanPlay.Current; }
        public string VipDueDate
        {
            get
            {
                return DateTime.FromFileTimeUtc(pbo.VipDueDate).ToLongDateString();
            }
        }

        public User(string path)
        {
            OnChanged += delegate { };
            localPath = path;
            token = "";
            pbo = new Mb.UserInfo();
            Load();
        }

        public void Reset()
        {
            version = VERSION_NOW;
            id = System.Guid.NewGuid().ToString();
            token = "";

            name = "游客";
            balance = 0;
            userLevel = 0;
            phone = "";
            email = "";

            pbo.VipDueDate = 0;
            pbo.VideoCanPlay = new Mb.CountPair() { Current = 0, Max = 5, };
            pbo.AudioCanPlay = new Mb.CountPair() { Current = 0, Max = 5, };
            pbo.NovelCanPlay = new Mb.CountPair() { Current = 0, Max = 5, };
        }

        public void Save(string path = null)
        {
            path = path??localPath;
            if (path == null) return;

            using(var stream = new FileStream(path, FileMode.Create))
            {
                pbo.WriteTo(stream);
            }
            logger.Log("save userdata", id, token);
        }

        public void Load()
        {
            bool loadOK = false;
            if (File.Exists(localPath))
            {
                logger.Log("load userdata", localPath);
                using(var stream = new FileStream(localPath, FileMode.Open))
                {
                    try
                    {
                        pbo.MergeFrom(stream);
                        if (pbo.Version != VERSION_NOW)
                        {
                            // FixCompatible();
                        }
                        loadOK = true;
                    }
                    catch (Exception e)
                    {
                        App.logger.Log("load userdata error", e.Message);
                    }
                }
            }

            if (!loadOK)
            {
                App.logger.Log("load userdata fail, reset!");
                Reset();
                Save();
            }

            App.logger.Log("load user ----->>", id, VideoCanPlayRemain);
        }

        void FixCompatible()
        {
            App.logger.Log($"UserInfo.LoadCompatible, version:[{this.version}->{VERSION_NOW}]");
            string id = this.id;
            this.Reset();
            this.id = id;
            this.version = VERSION_NOW;
        }

        public bool isVideoInHistory(int vid)
        {
            return this.SeenVideoList.Contains(vid);
        }

        public void addVideoToHistory(int vid)
        {
            if (!isVideoInHistory(vid))
            {
                SeenVideoList.Add(vid);
                VideoCanPlay.Current++;
            }
            else
            {
                SeenVideoList.Remove(vid);
                SeenVideoList.Add(vid);
            }
            logger.Log("addVideoToHistory", vid, VideoCanPlay.Current);
            Save();
        }

        public bool CanWatchVideo(int vid)
        {
            if (isVideoInHistory(vid)) return true;
            if (IsVip()) return true;
            if (VideoCanPlay.Current < VideoCanPlay.Max) return true;

            return false;
        }
        public bool IsVip()
        {
            // if (dueDate.Length > 0)
            // {
            //     DateTime dt = DateTime.Now;
            //     var nowTime = dt.ToString("yyyy-MM-dd");
            //     DateTime dt1 = Convert.ToDateTime(dueDate);
            //     DateTime dt2 = Convert.ToDateTime(nowTime);
            //     int compNum = DateTime.Compare(dt1, dt2);
            //     if (compNum >= 0)
            //     {
            //         return true;
            //     }
            // }
            return false;
        }
        //-///////////////////////////////////////
        #region me
        public static User me;
        public static void Init()
        {
            var mePath = App.HotStorageRoot.Substring(0, App.HotStorageRoot.LastIndexOf("/")) + "/user.json";
            if (startOption.GetBoolValue("clearUserInfo") && File.Exists(mePath))
            {
                App.logger.Log("User.ClearMe");
                File.Delete(mePath);
                Xamarin.Essentials.Preferences.Set("UserToken", "");
            }
            me = new User(mePath);
            me.token = Xamarin.Essentials.Preferences.Get("UserToken", "");
        }

        #endregion 

        static byte[] passwd = ASCIIEncoding.ASCII.GetBytes("xiaoting");

        void CopyPbo(Mb.UserInfo other)
        {
            var slist = pbo.SeenVideoList;
            pbo = other;
            // pbo.SeenVideoList.Add(slist);
        }

        public async void Fetch()
        {
            try
            {
                var res = await gRPC.Instance.ServiceClient.FetchAsync(new Mb.UserFetchRequest() { Id = this.id, Token = this.token });
                logger.Log("Fetch ok", res.Id, res.Name, res.VideoCanPlay.Current, "<<<<<");
                this.CopyPbo(res);
                this.Save();

                ServiceLocator.Instance.Get<IPopManager>().ShowToast("获取用户信息成功");

            }
            catch (Exception e)
            {
                logger.Log("Fetch fail", e);
                // mb.Droid.Util.ShowMsgBox($"获取用户数据失败", () => { });
            }
        }

        public void NotifyViewVideo(int vid, string vname, int openTime, int readyTime, int closeTime)
        {
            try
            {
                gRPC.Instance.ServiceClient.NotifyViewVideoAsync(new Mb.NotifyViewVideoRequest()
                {
                    Id = this.id,
                        VideoId = vid, VideoName = vname,
                        OpenTime = openTime, ReadyTime = readyTime, CloseTime = closeTime,
                });
                logger.Log("Notify ok", "<<<<<");
            }
            catch (Exception e)
            {
                logger.Log("Notify fail", e);
            }
        }

        public void Logout()
        {
            token = "";
            Xamarin.Essentials.Preferences.Set("UserToken", "");
            Save();
            OnChanged(this);
        }
        public async Task<string> Login(string phone, string password)
        {
            try
            {
                var res = await gRPC.Instance.ServiceClient.LoginAsync(new Mb.UserLoginRequest() { Phone = phone, Password = password, });
                logger.Log("login resul", res.Msg, res.Token, res.User);
                if (res.Msg?.Length == 0)
                {
                    this.token = res.Token;
                    Xamarin.Essentials.Preferences.Set("UserToken", this.token);
                    this.CopyPbo(res.User);
                    this.Save();
                    this.OnChanged(this);
                    return null;
                }
                else
                    return res.Msg;
            }
            catch (Exception e)
            {
                logger.Log("Login fail", e);
                return e.Message;
            }
        }

        public async Task<string> Register(string phone, string name, string password, string verifyCode)
        {
            try
            {
                var res = await gRPC.Instance.ServiceClient.RegisterAsync(new Mb.UserRegisterRequest() { Id = id, Phone = phone, Name = name, Password = password, VerifyCode = verifyCode });
                if (res.Ok)
                {
                    this.name = name;
                    this.phone = phone;
                    return null;
                }
                else
                    return res.Msg;
            }
            catch (Exception e)
            {
                return e.Message;
            }
        }

        public void Fetch2()
        {
            var baseUrl = App.CurrentServerUrl;
            var url = $"http://{baseUrl}/user/fetch";
            logger.Log("fetch user", url);

            var body = LitJson.JsonMapper.ToJson(new { token = libmp.User.me.token, id = libmp.User.me.id });
            var ebody = mb.Util.Encrypt(body, passwd);
            var dbody = mb.Util.Decrypt(ebody, passwd);
            logger.Log("body---------", body, ebody, dbody == body);
            body = "mbdata:" + ebody;
            body = ebody;
            Http.Option opt = null;
            var res = Http.MakeRequest(opt = new Http.Option
            {
                Url = url,
                    Method = HttpMethod.Post,
                    Body = body,
                    ContentType = "text/mbec",
                    TimeoutSec = 5,
                    OnFinish = (ok, code, msg) =>
                    {
                        try
                        {
                            if (code != 200)
                            {
                                logger.Log("User.Fetch failed/1", ok, code);
                                this.token = "";
                                return;
                            }
                            logger.Log("User.Fetch header", opt.Response.Content.Headers.ContentType);
                            msg = mb.Util.Decrypt(msg, passwd);

                            var jmsg = LitJson.JsonMapper.ToObject(msg);
                            var err = jmsg.ContainsKey("error") ? jmsg["error"].ToString() : null;
                            if (err != null)
                            {
                                logger.Log("User.Fetch failed/2", err);
                                this.token = "";
                            }
                            else
                            {
                                logger.Log("finish loadMe", url, ok, code, msg);
                                var newUser = LitJson.JsonMapper.ToObject<libmp.User>(msg);
                                this.name = newUser.name;
                                this.phone = newUser.phone;
                                this.email = newUser.email;
                                // this.viewsNums = newUser.viewsNums;
                                // this.balance = newUser.balance;
                                // this.userLevel = newUser.userLevel;
                                // this.userHeadPic = newUser.userHeadPic;
                                // this.dueDate = newUser.dueDate;
                                // this.changeDate = newUser.changeDate;

                                this.Save();
                                this.OnChanged(this);
                            }
                        }
                        catch (Exception e)
                        {
                            logger.Log("User.Fetch failed/3", e, e.Data["StackTrace"]?.ToString(), msg);
                            this.token = "";
                        }
                    },
            });
        }

        public void Notify(string type, string extra = null)
        {
            var url = $"http://{UserServer}/user/notify";
            var jobj = new JsonData();
            jobj["id"] = this.id;
            jobj["type"] = type;
            jobj["deviceInfo"] = Util.GetHardwareInfo();

            if (type == "register")
            {
                jobj["phone"] = this.phone;
                jobj["name"] = this.name;
            }

            if (type == "login") { }

            if (extra != null)
                jobj["extra"] = JsonMapper.ToObject(extra);

            logger.Log("notify >>>>>", type, id, url, extra);
            var res = Http.MakeRequest(new Http.Option
            {
                Url = url,
                    Method = HttpMethod.Post,
                    Body = LitJson.JsonMapper.ToJson(jobj),
                    OnFinish = (ok, code, msg) =>
                    {
                        // logger.Log("notify <<<<<", ok, code, msg);
                    }
            });
        }
        public void UpdateViewNums(string id, int viewsNums, bool changeMax)
        {
            var baseUrl = App.CurrentServerUrl;
            var body = LitJson.JsonMapper.ToJson(new { id = id, viewsNums = viewsNums, changeMax = changeMax });
            var url = $"http://{baseUrl}/user/updateViewNums";
            var res = Http.MakeRequest(new Http.Option
            {
                Url = url,
                    Method = HttpMethod.Post,
                    Body = body,
                    OnFinish = (ok, code, msg) =>
                    {
                        logger.Log("msg:" + msg);
                        if (code == 500)
                        {
                            logger.Log("----更新错误");
                        }
                        else
                        {
                            logger.Log("----更新成功");
                        }

                    },
                    OnProgress = (total, now) =>
                    {
                        logger.Log("progress", total, now);
                    }
            });
        }

        string UserServer
        {
            get
            {
                // var servers = App.AServerLoader.GetAvailabeServers(ServerLoader.ServerType.User);
                return App.CurrentServerUrl;
            }
        }
    }

}